<template>
  <el-dialog v-model="baseDialogVisible" destroy-on-close :title="'title'" :width="width" ref="baseDialogRef" @open="handleOpen" v-bind="otherAttr">
    <!-- 头部内容插槽 -->
    <template #header>
      <slot name="header"></slot>
    </template>
    <!-- 默认内容插槽 -->
    <slot></slot>
    <!-- 尾部内容插槽 -->
    <template #footer>
      <slot name="footer">
        <el-button @click="baseDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="handleConfirm">确定</el-button>
      </slot>
    </template>
  </el-dialog>
</template>

<script setup lang="ts">
  import { ElDialog } from 'element-plus'
  import { onBeforeMount, onBeforeUnmount, ref, reactive, computed, nextTick } from 'vue'
  const baseDialogRef = ref<InstanceType<typeof ElDialog>>()
  // 保存鼠标点击位置
  const mousePosition = reactive({
    x: 0,
    y: 0,
  })
  const baseDialogVisible = computed({
    get() {
      return props.visible
    },
    set(val: boolean) {
      emit('update:visible', val)
    },
  })
  interface Props {
    visible: boolean // 控制dialog显示与隐藏
    title?: string // dialog标题
    width?: number // 宽度
    otherAttr?: { [key: string]: any } // 其他element-plus的dialog支持的属性
  }

  const props = withDefaults(defineProps<Props>(), {
    // 设置默认值
    visible: false,
    title: '默认标题',
    width: 500,
  })

  const emit = defineEmits<{
    (e: 'update:visible', visible: boolean): void
    (e: 'confirm'): void
  }>()

  function handleConfirm() {
    emit('confirm')
  }

  function handleOpen() {
    const node: HTMLElement = baseDialogRef.value?.dialogContentRef.$el
    const computedStyle = getComputedStyle(node) // 样式集合
    let width // 对话框宽度
    if (/px/g.test(computedStyle.width)) {
      // 如果宽度是像素类型
      // 正则替换像素
      width = Number(computedStyle.width.replace(/px/g, ''))
    } else {
      // 宽度为百分比类型
      // 正则替换百分比并转化为数字格式
      width = document.documentElement.clientWidth * (Number(computedStyle.width.replace(/%/g, '')) / 100)
    }
    const top = computedStyle.marginTop.replace(/px/g, '') // 对话框距离顶部的距离

    // 计算变换偏移
    // element-plus 中，对话框默认是居中的
    const transformLeft = mousePosition.x - (document.documentElement.clientWidth - width) / 2 // 本质上为对话框左上角的 x 距离触发点 x 的距离
    const transformTop = mousePosition.y - Number(top) // 本质上为对话框左上角的 y 距离触发点 y 的距离
    node.style.transformOrigin = `${transformLeft}px ${transformTop}px`
  }

  function clickHandler(e: any) {
    mousePosition.x = e.x
    mousePosition.y = e.y
  }

  onBeforeMount(() => {
    document.documentElement.addEventListener('click', clickHandler, true) // 事件捕获
  })

  onBeforeUnmount(() => {
    document.documentElement.removeEventListener('click', clickHandler, true)
  })
</script>
